﻿#pragma once

#include <QPointF>
#include <QPolygonF>
#include <QPainterPath>
#include <QFont>

#if __cplusplus >= 202002L
#define DEFINE_OPERATOR_EQ(type) bool operator==(const type&) const = default;
#else
#define DEFINE_OPERATOR_EQ(_)
#endif

constexpr Qt::FillRule FILL_RULE = Qt::OddEvenFill;
constexpr char FILL_RULE_STR[] = "evenodd";

class SvgShape;
class SvgLine;
class SvgRect;
class SvgEllipse;
class SvgPolygon;
class SvgText;
class SvgPath;

using SvgShapeVec = vector<unique_ptr<SvgShape>>;

template <typename R>
class ShapeVisitor
{
protected:
	virtual ~ShapeVisitor() = default;
	// 这些其实用 const 比较好，但是我懒, 而且如果之后如果把 文本化 轻量化的话，这玩意其实作用就不大了。。。
	virtual R _VisitLine(SvgLine& line) = 0;
	virtual R _VisitRect(SvgRect& rect) = 0;
	virtual R _VisitEllipse(SvgEllipse& ellipse) = 0;
	virtual R _VisitPolygon(SvgPolygon& polygon) = 0;
	virtual R _VisitText(SvgText& text) = 0;
	virtual R _VisitPath(SvgPath& path) = 0;

	friend class SvgShape;
	friend class SvgLine;
	friend class SvgRect;
	friend class SvgEllipse;
	friend class SvgPath;
	friend class SvgText;
	friend class SvgPolygon;
};

class SvgShape
{
public:
	SvgShape();

	QPen& Pen() { return m_pen; }
	QBrush& Brush() { return m_brush; }
	const QPen& Pen() const { return m_pen; }
	const QBrush& Brush() const { return m_brush; }

	DEFINE_OPERATOR_EQ(SvgShape)

public:
	virtual ~SvgShape() = default;

	virtual void Paint(QPainter& painter) const = 0;
	virtual void Translate(const QPointF& p) = 0;
	// 这个函数是图形拖拽的核心之一，他的作用是将图形按原来的外接矩形平移到 这个参数矩形的位置，并进行缩放。
	// 如果 rect 的 height 或者 width 为负值，会进行一次对应轴的翻转。
	virtual void Translate(const QRectF& rect) = 0;
	virtual QRectF BoundingRect() const = 0;
	virtual bool PointInShape(const QPointF& pos) const = 0;

	virtual void Accept(ShapeVisitor<void>& visitor) = 0;
	// 这里有个利用模板和继承的有趣实现方案，https://stackoverflow.com/questions/36189838/c-elegantly-clone-derived-class-by-calling-base-class
	virtual unique_ptr<SvgShape> Clone() = 0;

	// 这个是外接矩形考虑线宽后的样子
	virtual QRectF VisualRect() const;

protected:
	QPen m_pen;
	QBrush m_brush;
};

class SvgLine : public SvgShape
{
public:
	explicit SvgLine(const QLineF& line) : m_line(line) {}

	const QLineF& Line() const { return m_line; }
	void SetLine(const QLineF& line) { m_line = line; }

	DEFINE_OPERATOR_EQ(SvgLine)

public:
	void Paint(QPainter& painter) const override;
	QRectF BoundingRect() const override;
	bool PointInShape(const QPointF& pos) const override;
	void Translate(const QPointF& p) override;
	void Accept(ShapeVisitor<void>& visitor) override;
	void Translate(const QRectF& rect) override;
	unique_ptr<SvgShape> Clone() override;

private:
	QLineF m_line;
};

class SvgRect : public SvgShape
{
public:
	SvgRect(const QRectF& rect) : m_rect(rect) { m_pen.setWidth(2); }

	const QRectF& Rect() const { return m_rect; }
	void SetRect(const QRectF& rect) { m_rect = rect; }

	DEFINE_OPERATOR_EQ(SvgRect)
public:
	void Paint(QPainter& painter) const override;
	QRectF BoundingRect() const override;
	bool PointInShape(const QPointF& pos) const override;
	void Translate(const QPointF& p) override;
	void Accept(ShapeVisitor<void>& visitor) override;
	void Translate(const QRectF& rect) override;
	unique_ptr<SvgShape> Clone() override;

private:
	// p0 是左上角点， p1 是右下角点
	QRectF m_rect;
};

class SvgEllipse : public SvgShape
{
public:
	explicit SvgEllipse(const QRectF& rect) : m_rect(rect) { m_pen.setWidth(2); }

	QPointF Center() const { return m_rect.center(); }
	double Rx() const { return m_rect.width() / 2; }
	double Ry() const { return m_rect.height() / 2; }

	DEFINE_OPERATOR_EQ(SvgEllipse)
public:
	void Paint(QPainter& painter) const override;
	QRectF BoundingRect() const override;
	bool PointInShape(const QPointF& p) const override;
	void Translate(const QPointF& p) override;
	void Accept(ShapeVisitor<void>& visitor) override;
	void Translate(const QRectF& rect) override;
	unique_ptr<SvgShape> Clone() override;

public:
	static SvgEllipse Circle(const QPointF& center, double radius);

private:
	QRectF m_rect;
};

class SvgPolygon : public SvgShape
{
public:
	explicit SvgPolygon(QPolygonF polygon) : m_polygon(std::move(polygon)) {}

	QPolygonF& Polygon() { return m_polygon; }

	DEFINE_OPERATOR_EQ(SvgPolygon)
public:
	void Paint(QPainter& painter) const override;
	QRectF BoundingRect() const override;
	bool PointInShape(const QPointF& p) const override;
	void Translate(const QPointF& p) override;
	void Accept(ShapeVisitor<void>& visitor) override;
	void Translate(const QRectF& rect) override;
	unique_ptr<SvgShape> Clone() override;

private:
	QPolygonF m_polygon;
};

class SvgPath : public SvgShape
{
public:
	SvgPath(QPainterPath path) : m_path(std::move(path)) {}

	const QPainterPath& Path() const { return m_path; }

	DEFINE_OPERATOR_EQ(SvgPath)
public:
	void Paint(QPainter& painter) const override;
	QRectF BoundingRect() const override;
	bool PointInShape(const QPointF& p) const override;
	void Translate(const QPointF& p) override;
	void Accept(ShapeVisitor<void>& visitor) override;
	void Translate(const QRectF& rect) override;
	unique_ptr<SvgShape> Clone() override;

public:
	static unique_ptr<SvgPath> PointsToPath(const std::vector<QPointF>& points);

private:
	QPainterPath m_path;
};

class SvgText : public SvgShape
{
public:
	SvgText(const QPointF& p, const QFont& font, QString text) : m_pos(p), m_text(std::move(text)), m_font(font) {}
	SvgText(const QPointF& p, const QFont& font) : m_pos(p), m_font(font) {}

	void SetText(const QString& text) { m_text = text; }
	const QString& Text() const { return m_text; }
	// 是左下角点
	const QPointF& Pos() const { return m_pos; }
	const QFont& Font() const { return m_font; }

	DEFINE_OPERATOR_EQ(SvgText)
public:
	void Paint(QPainter& painter) const override;
	QRectF BoundingRect() const override;
	bool PointInShape(const QPointF& p) const override;
	void Translate(const QPointF& p) override;
	void Accept(ShapeVisitor<void>& visitor) override;
	void Translate(const QRectF& rect) override;
	unique_ptr<SvgShape> Clone() override;

public:
	static QSizeF ContentSize(const QString& content, const QFont& font);

private:
	QPointF m_pos; // 是左下角点
	QString m_text;
	QFont m_font;
};

